package view;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.io.IOException;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JColorChooser;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.filechooser.FileFilter;

import common.Change;
import common.Utils;

import controller.DrawingController;
import controller.Pallette;
import controller.State;

import model.DrawObject;
import model.DrawingModel;


public class DrawingView extends JPanel 
	implements MouseListener, MouseMotionListener, KeyListener, Observer, ActionListener, WindowListener
{
	private static final long serialVersionUID = 2393440202135970896L;
	
	DrawingModel model;
	DrawingController controller;
	
	private GeometryDescriptor selectedObjDesc;
	private Color selectedColor;
	private int selectedThickness;
	
	private Point lastClick;
	private Point lastMove;
	
	
	
	private JFrame myFrame;
	
	public DrawingView(DrawingModel mod)
	{
		selectedObjDesc = new GeometryDescriptor();
		selectedColor = new Color(0, 0, 0);
		
		model = mod;
		mod.addObserver(this);
		
		controller = new DrawingController(mod, this);
		
		addMouseListener(this);
		addMouseMotionListener(this);
		addKeyListener(this);
		setFocusable(true);
	}
	
	public void paint (Graphics g)
	{
		// paint the bounding box
		super.paint(g);
		
		// paint the objects
		int max = model.nDrawObjects();
		for (int i=0 ; i<max; i++)
		{
			ViewObject.convert( model.getDrawObject(i), g ).Draw(g);
		}
		
		// paint the control points
		if(selectedObjDesc.getIndex() != -1)
		{
			this.getSelectedObject().DrawControlPoints(g);
		}
		
	}
	
	public GeometryDescriptor pointGeometry(Point mouseLoc)
	{	
		GeometryDescriptor gDesc = new GeometryDescriptor();
		
		gDesc = getControlPointGeometry(mouseLoc);
		
		if (gDesc != null)
			return gDesc;
		
		gDesc = getModelGeometry(mouseLoc);
		
		gDesc.setMoved3(moved3());
		
		return gDesc;
	}
	
	private GeometryDescriptor getControlPointGeometry(Point mouseLoc)
	{
		GeometryDescriptor gDesc = new GeometryDescriptor();
		
		if (selectedObjDesc.getIndex() == -1)
			return null;
		
		int controlPoint = getSelectedObject().calcControlPoint(mouseLoc);
		if (controlPoint == 0)
			return null;
		
		gDesc.setIndex(selectedObjDesc.getIndex());
		gDesc.setCharIndex( getSelectedObject().getSelCharIndex() );
		gDesc.setControlPoint( controlPoint );
		
		return gDesc;
	}
	
	private GeometryDescriptor getModelGeometry(Point mouseLoc) 
	{
		GeometryDescriptor gDesc = new GeometryDescriptor();
		
		for (int i=model.nDrawObjects()-1; i>-1; i--) // counting down
		{
			ViewObject tmpObj = ViewObject.convert( model.getDrawObject(i) , this.getGraphics() );
			if (tmpObj.containsPoint(mouseLoc))
			{
				gDesc.setIndex(i);
				gDesc.setCharIndex(tmpObj.getSelCharIndex());
				break;
			}
		}
		
		return gDesc;
	}
	
	public boolean moved3()
	{
		if (lastClick == null || lastMove == null)
			return false;
		
		float width = lastMove.x - lastClick.x;
		float height = lastMove.y - lastClick.y;
		
		float distSq = width*width + height*height;
		
		if (distSq >= 9)
			return true;
		
		return false;
	}
	
	public ViewObject getSelectedObject()
	{
		if (selectedObjDesc.getIndex() == -1)
			return null;
		
		ViewObject tmp = ViewObject.convert( model.getDrawObject(selectedObjDesc.getIndex()) , this.getGraphics());
		tmp.sel_char_index = selectedObjDesc.getCharIndex();
		
		return tmp;
	}
	
	@Override
	public void update(Observable arg0, Object arg1) 
	{		
		super.repaint();
	}
	
	
	public static JMenuBar getMenu(ActionListener view)
	{
		// 'File' Menu
		JMenuItem mi1 = new JMenuItem("Open");
		mi1.addActionListener(view);
		JMenuItem mi2 = new JMenuItem("Save");
		mi2.addActionListener(view);
		JMenuItem mi3 = new JMenuItem("Save As");
		mi3.addActionListener(view);
		
		JMenu m1 = new JMenu("File");
		m1.add(mi1);
		m1.add(mi2);
		m1.add(mi3);
		
		// 'Set' Menu
		JMenuItem mi4 = new JMenuItem("Color");
		mi4.addActionListener(view);
		JMenuItem mi5 = new JMenuItem("Thickness");
		mi5.addActionListener(view);
		
		JMenu m2 = new JMenu("Set");
		m2.add(mi4);
		m2.add(mi5);
		
		// Put it all together in the menu bar
		JMenuBar mb = new JMenuBar();
		mb.add(m1);
		mb.add(m2);
		
		return mb;
	}
	
	@Override
	public void actionPerformed(ActionEvent e)
	{			
		if (e.getActionCommand() == "Open")
			openFile();
		else if (e.getActionCommand() == "Save")
			saveFile();
		else if (e.getActionCommand() == "Save As")
			saveFileAs();
		else if (e.getActionCommand() == "Color")
			setColor();
		else if (e.getActionCommand() == "Thickness")
			setThickness();
		else if (e.getActionCommand() == "Select")
			controller.changeState(Pallette.SELECT);
		else if (e.getActionCommand() == "Text")
			controller.changeState(Pallette.TEXT);
		else if (e.getActionCommand() == "Line")
			controller.changeState(Pallette.LINE);
		else if (e.getActionCommand() == "Curve")
			controller.changeState(Pallette.CURVE);
		else if (e.getActionCommand() == "EmptyRect")
			controller.changeState(Pallette.E_RECT);
		else if (e.getActionCommand() == "FilledRect")
			controller.changeState(Pallette.F_RECT);
		else if (e.getActionCommand() == "EmptyEllipse")
			controller.changeState(Pallette.E_ELLIP);
		else if (e.getActionCommand() == "FilledEllipse")
			controller.changeState(Pallette.F_ELLIP);
		
	}
	
	private void setThickness() 
	{
		String tmpStr = JOptionPane.showInputDialog(this, "Enter a thickness in pixels:");
		
		int thickness = 1;
		
		try
		{
			thickness = Integer.parseInt(tmpStr);
		}
		catch (Exception e)
		{
			return;
		}
		
		selectedThickness = thickness;
		controller.setSelectionThickness(thickness);
		
	}

	private void setColor() 
	{		
		Color tmpColor = JColorChooser.showDialog(this, "Pick a Color", selectedColor);
		
		if (tmpColor != null)
		{
			selectedColor = tmpColor;
			controller.setSelectionColor(selectedColor);
		}
	}

	public void saveFileAs()
	{
		File f = getFileFromDialog(false);
		if (f == null)
			return;
		
		String path = null;
		
		try 
		{
			path = f.getCanonicalPath();
		} 
		catch (IOException e) 
		{
			JOptionPane.showMessageDialog(this, "Error: " + e.getMessage());
			return;
		}
		
		controller.saveAs(path);
	}

	public void saveFile() {
		
		if (model.getFileName().equals(""))
		{
			saveFileAs();
			return;
		}
				
		controller.save();
	}

	private void openFile() 
	{
		
		File f = getFileFromDialog(true);
		if (f == null)
			return;
		
		String ext = getExtension(f);
		if (!f.exists() || !ext.equals(Utils.IMG_EXT) )
			return;
		
		String can_path;
		try
		{
			can_path = f.getCanonicalPath();
			controller.openFile(can_path);
		} 
		catch (IOException e) 
		{
			e.printStackTrace();
		}
		
		
		
	}
	
	private File getFileFromDialog(boolean open)
	{
		JFileChooser fc = new JFileChooser(System.getProperty("user.dir"));
		FileFilter filter = new imgFilter();
		fc.setFileFilter(filter);
		fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
		
		int result = 0;
		if (open)
			result = fc.showOpenDialog(this);
		else
			result = fc.showSaveDialog(this);
		
		if (result == JFileChooser.CANCEL_OPTION)
			return null;
		
		return fc.getSelectedFile();
	}
	
	public static String getOpenFileFromDialog()
	{
		JFileChooser fc = new JFileChooser(System.getProperty("user.dir"));
		FileFilter filter = new imgFilter();
		fc.setFileFilter(filter);
		fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
		
		int result = 0;
		result = fc.showOpenDialog(null);
	
		if (result == JFileChooser.CANCEL_OPTION)
			return "";
		
		String path = "";
		
		try 
		{
			path = fc.getSelectedFile().getCanonicalPath();
		} 
		catch (IOException e) 
		{
			JOptionPane.showMessageDialog(null, "File could not be loaded!");
		}
		
		return path;
	}
	
	
	/*
	 * ****************************************************
	 * 				Methods for input events
	 * ****************************************************
	 * 
	 */
		
	

	@Override
	public void mousePressed(MouseEvent e)
	{
		lastClick = e.getPoint();
		controller.mouseDown(e);
	}

	@Override
	public void mouseReleased(MouseEvent e)
	{
		lastClick = null;
		controller.mouseUp(e);
	}	
	
	@Override
	public void mouseDragged(MouseEvent e)
	{
		lastMove = e.getPoint();
		controller.mouseMove(e);
	}
		
	@Override
	public void keyPressed(KeyEvent e)
	{
		controller.keyPressed(e);
	}

	/*
	 * ****************************************************
	 * 					Unused Events
	 * ****************************************************
	 * 
	 */
	
	@Override
	public void mouseEntered(MouseEvent e)
	{	}

	@Override
	public void mouseExited(MouseEvent e)
	{	}
	
	@Override
	public void mouseClicked(MouseEvent e)
	{	}
	
	@Override
	public void keyTyped(KeyEvent e) 
	{	}

	@Override
	public void keyReleased(KeyEvent e) 
	{	}
	
	@Override
	public void mouseMoved(MouseEvent e)
	{	}
	

	/*
	 * ****************************************************
	 * 					Getters & Setters
	 * ****************************************************
	 * 
	 */
	
	public void setModel(DrawingModel mod)
	{
		model = mod;
	}
	
	public JFrame getJFrame()
	{
		return myFrame;
	}
	
	public void setJFrame(JFrame frm)
	{
		myFrame = frm;
	}
	
	public GeometryDescriptor getSelectedObjDesc() 
	{
		return selectedObjDesc;
	}

	public void setSelectedObjDesc(GeometryDescriptor selectedObjDesc) 
	{
		this.selectedObjDesc = selectedObjDesc;
	}

	
	private String getExtension(File f)
	{
		String ext = "";
		String name = f.getName();
		int i = name.lastIndexOf('.');
		
		if (i == -1)
			return ext;
		
		ext = name.substring(i);
		return ext;
	}
	
	

	@Override
	public void windowActivated(WindowEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void windowClosed(WindowEvent e) 
	{
		// TODO Auto-generated method stub
	}

	@Override
	public void windowClosing(WindowEvent e) 
	{
		controller.closeView();		
	}

	@Override
	public void windowDeactivated(WindowEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void windowDeiconified(WindowEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void windowIconified(WindowEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void windowOpened(WindowEvent e) {
		// TODO Auto-generated method stub
		
	}	
	
	
}



